#define _POSIX_SOURCE
#define _BSD_SOURCE

#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include "rngs.h"

#ifndef MAX_PLAYER
#define MAX_PLAYER 4
#endif

#ifndef SMALL
#define SMALL 10
#endif

#ifndef MEDIUM
#define MEDIUM 100
#endif

int initialize(struct gameState *state, int *handPos, int *choice)
{
	int i;

	/* Set Random game numbers */
	state->numPlayers = floor(Random() * (MAX_PLAYER - 1)) + 2;
	state->whoseTurn = floor(Random() * state->numPlayers);
	state->deckCount[state->whoseTurn] = floor(Random() * MEDIUM);
	state->discardCount[state->whoseTurn] = floor(Random() * MEDIUM);
	state->handCount[state->whoseTurn] = floor(Random() * MEDIUM) + 1;
	state->playedCardCount = floor(Random() * SMALL);
	state->coins = 0;

	/* Set Supply Count */
	for (i = 0; i < treasure_map + 1; i++) {
		state->supplyCount[i] = floor(Random() * SMALL);
	}

	/* Set 1 card to be feast */
	*handPos = floor(Random() * state->handCount[state->whoseTurn]);
	state->hand[state->whoseTurn][*handPos] = feast;

	/* Set random cards in hand, making sure none other cards are feast */
	for (i = 0; i < state->handCount[state->whoseTurn]; i++) {
		if (i != *handPos) {
			state->hand[state->whoseTurn][i] = floor(Random() * treasure_map);
			while (state->hand[state->whoseTurn][i] == feast) {
				state->hand[state->whoseTurn][i] = floor(Random() * treasure_map);
			}

			/* Set up coins based on cards in hand */
			if (state->hand[state->whoseTurn][i] == copper) {
				state->coins += 1;
			} else if (state->hand[state->whoseTurn][i] == silver) {
				state->coins += 2;
			} else if (state->hand[state->whoseTurn][i] == gold) {
				state->coins += 3;
			}
		}
	}

	/* Set a random choice, make sure that feast is playable*/
	// THERE IS A BUG IN THE CODE WHERE AN INFINITE LOOP IS SET
	// IF THERE IS NO LEGAL MOVE AND THE FEAST CARD IS PLAYED!!!
	// DISABLE THE WHILE AND IF BLOCK AT YOUR OWN RISK!!!
	*choice = floor(Random() * treasure_map);
	while (getCost(*choice) > 5) {
		*choice = floor(Random() * treasure_map);
	}
	if (state->supplyCount[*choice] <= 0) {
		state->supplyCount[*choice] = 1;
	}

	return 0;
}

int checkFeast(struct gameState *post, int handPos, int choice, int n, int verboseFlag)
{
	struct gameState pre;

	assert(post->supplyCount[choice] > 0);
	assert(getCost(choice) <= 5);

	/* Save initial preconditions */
	int preDeckCount = post->deckCount[post->whoseTurn];
	int preDiscardCount = post->discardCount[post->whoseTurn];
	//int preHandCount = post->handCount[post->whoseTurn];
	//int prePlayedCount = post->playedCardCount;
	//int preCoinCount = post->coins;
	int preSupplyCount = post->supplyCount[choice];

	/* Copy memory located at post to memory located at pre */
	memcpy(&pre, post, sizeof(struct gameState));

	cardEffect(feast, choice, -1, -1, post, handPos, NULL);

	if (pre.supplyCount[choice] > 0 && getCost(choice) <= 5) {

		/* Feast should not affect the deck count */
		assert(post->deckCount[post->whoseTurn] == preDeckCount);

		/* Feast should gain a card from the supply */
		assert(post->supplyCount[choice] == preSupplyCount - 1);
		assert(post->discardCount[post->whoseTurn] == preDiscardCount + 1);
		assert(post->discard[post->whoseTurn][post->discardCount[post->whoseTurn] - 1] == choice);

		/* Feast should be removed from the hand */
		//assert(post->handCount[post->whoseTurn] == preHandCount - 1);

		/* Feast should return the coin count to normal */
		assert(post->coins == 5);

		/* Set up pre to have expected conditions */
		pre.discard[pre.whoseTurn][pre.discardCount[pre.whoseTurn]] = choice;
		pre.discardCount[pre.whoseTurn] += 1;
		pre.supplyCount[choice] -= 1;
		pre.coins = 5;
		//discardCard(handPos, pre.whoseTurn, &pre, 1);

		// NOTE THAT THE COMMENTED CODES WILL CAUSE THE ASSERT 
		// TO FAIL BECAUSE THESE ARE BUGS IN THE CARD'S CODE
	} else {
		printf("\tDid not execute\n");
	}

	if (memcmp(&pre, post, sizeof(struct gameState)) != 0) {
		printf("\t!!! TEST #%5d FAIL !!!\n", n);
	}

	return 0;
}

int main(int argc, char **argv)
{
	int i, choice, handPos;
	int verboseFlag = 0;
	struct gameState G;

	/* Set Random Number Generator */
	SelectStream(2);
	PutSeed(3);

	if (argc > 1 && strcmp(argv[1], "-v") == 0) {
		verboseFlag = 1;
	}

	printf("Testing cardEffect feast.\n");

	for (i = 1; i <= 20000; i++) {
		initialize(&G, &handPos, &choice);

		if (verboseFlag) {
			printf("Test #%5d: plyr=%d dck=%3d hnd=%3d dsc=%3d hndPos=%3d choice=%3d\n", 
					i, G.whoseTurn, G.deckCount[G.whoseTurn], G.handCount[G.whoseTurn], G.discardCount[G.whoseTurn], handPos, choice);
		}

		checkFeast(&G, handPos, choice, i, verboseFlag);
	
		/*
		int v = floor(Random() * treasure_map);
		printf("v: %d\n", v);
		if (v > treasure_map) {
			printf("\tFail\n");
		}
		*/
	}

	printf ("ALL TESTS OK\n");

	return 0;
}